﻿//-----------------------------------------------------------------------
// <copyright file="Product.cs" company="Craig Longford (deltawolf7@gmail.com)">
//     Copyright (c) Craig Longford. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------

namespace Delta.Mercury.Business.Commerce
{
    using System;
    using System.Configuration;
    using System.Globalization;
    using Delta.Data.DataExpress;
    using Delta.Mercury.Business.Exceptions;

    /// <summary>
    /// Representaion of a product.
    /// </summary>
    [Serializable]
    public class Product : IdentifierBase, IDataServicable, IProduct
    {
        #region Members
        /// <summary>
        /// Supplier part number
        /// </summary>
        private string supplierPartNumber;

        /// <summary>
        /// Manuafcturer part number
        /// </summary>
        private string manufacturerPartNumber;

        /// <summary>
        /// Product name
        /// </summary>
        private string name;

        /// <summary>
        /// Product SKU
        /// </summary>
        private string sku;
        #endregion

        #region Constructors
        /// <summary>
        /// Initializes a new instance of the Product class.
        /// </summary>
        public Product()
            : this(string.Empty, string.Empty, 0.0M, 0.0M, 0, true)
        {       
        }

        /// <summary>
        /// Initializes a new instance of the Product class.
        /// </summary>
        /// <param name="sku">Product sku</param>
        /// <param name="name">Product name</param>
        /// <param name="price">Product price</param>
        /// <param name="stock">Product stock</param>
        public Product(string sku, string name, decimal price, int stock)
            : this(sku, name, price, 0.0M, stock, true)
        {
        }

        /// <summary>
        /// Initializes a new instance of the Product class.
        /// </summary>
        /// <param name="sku">Product sku</param>
        /// <param name="name">Product name</param>
        /// <param name="price">Product price</param>
        /// <param name="cost">Product cost</param>
        /// <param name="stock">Product stock</param>
        public Product(string sku, string name, decimal price, decimal cost, int stock)
            : this(sku, name, price, cost, stock, true)
        {
        }

        /// <summary>
        /// Initializes a new instance of the Product class.
        /// </summary>
        /// <param name="sku">Product sku</param>
        /// <param name="name">Product name</param>
        /// <param name="price">Product price</param>
        /// <param name="cost">Product cost</param>
        /// <param name="stock">Product stock</param>
        /// <param name="taxable">Product taxable</param>
        public Product(string sku, string name, decimal price, decimal cost, int stock, bool taxable)
            : base()
        {
            this.Cost = cost;
            this.Description = string.Empty;
            this.ManufacturerGuid = Guid.Empty;
            this.manufacturerPartNumber = string.Empty;
            this.name = name;
            this.Price = price;
            this.sku = sku;
            this.SupplierGuid = Guid.Empty;
            this.Stock = stock;
            this.supplierPartNumber = string.Empty;
            this.Taxable = taxable;
        }
        #endregion

        #region Properties
        /// <summary>
        /// Gets or sets the Stock Keeping Unit
        /// </summary>
        public virtual string SKU
        {
            get
            {
                return this.sku;
            }

            set
            {
                if (value.Length < 1)
                {
                    throw new InvalidLengthException("Product sku must be a minimum of 1 characters.");
                }

                if (value.Length > 20)
                {
                    throw new InvalidLengthException("Product sku must be a maximum of 20 characters.");
                }

                this.sku = value.ToUpper();
            }
        }

        /// <summary>
        /// Gets or sets the product name
        /// </summary>
        public virtual string Name
        {
            get
            {
                return this.name;
            }

            set
            {
                if (value.Length < 1)
                {
                    throw new InvalidLengthException("Product name must be a minimum of 1 characters.");
                }

                if (value.Length > 50)
                {
                    throw new InvalidLengthException("Product name must be a maximum of 50 characters.");
                }

                TextInfo info = CultureInfo.CurrentCulture.TextInfo;
                this.name = info.ToTitleCase(value);
            }
        }

        /// <summary>
        /// Gets or sets the description
        /// </summary>
        public virtual string Description { get; set; }

        /// <summary>
        /// Gets or sets the cost price
        /// </summary>
        public virtual decimal Cost { get; set; }

        /// <summary>
        /// Gets or sets the sale price
        /// </summary>
        public virtual decimal Price { get; set; }

        /// <summary>
        /// Gets or sets the Manufacturer Part Number
        /// </summary>
        public virtual string ManufacturerPartNumber
        {
            get
            {
                return this.manufacturerPartNumber;
            }

            set
            {
                if (value.Length > 20)
                {
                    throw new InvalidLengthException("Product manufacturer part number must be a maximum of 20 characters.");
                }

                this.manufacturerPartNumber = value.ToUpper();
            }
        }

        /// <summary>
        /// Gets or sets the manufacturer unique identifier
        /// </summary>
        public virtual Guid ManufacturerGuid { get; set; }

        /// <summary>
        /// Gets or sets the supplier unqiue identifier
        /// </summary>
        public virtual Guid SupplierGuid { get; set; }

        /// <summary>
        /// Gets or sets the supplier Part Number
        /// </summary>
        [StringValidator(MaxLength = 20)]
        public virtual string SupplierPartNumber
        {
            get
            {
                return this.supplierPartNumber;
            }

            set
            {
                if (value.Length > 20)
                {
                    throw new InvalidLengthException("Product supplier part number must be a maximum of 20 characters.");
                }

                this.supplierPartNumber = value.ToUpper();
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the product is active or not.
        /// </summary>
        public virtual bool Active { get; set; }

        /// <summary>
        /// Gets or sets the stock.
        /// </summary>
        public virtual int Stock { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether the product is taxable
        /// </summary>
        public virtual bool Taxable { get; set; }
        #endregion

        #region Static Load Methods
        /// <summary>
        /// Load product by unique identifier
        /// </summary>
        /// <param name="guid">Unique identifier</param>
        /// <param name="showDeleted">Show deleted</param>
        /// <returns>Filled product</returns>
        public static Product Load(Guid guid, bool showDeleted)
        {
            Parameters parmeters = new Parameters();
            parmeters.Add(new Parameter("Guid", guid));
            parmeters.Add(new Parameter("Deleted", showDeleted));
            return DataAccess.GetSingle<Product>("LoadProduct", System.Data.CommandType.StoredProcedure, parmeters);
        }

        /// <summary>
        /// Load product by sku
        /// </summary>
        /// <param name="sku">Product SKU</param>
        /// <param name="showDeleted">Show deleted</param>
        /// <returns>Filled product</returns>
        public static Product Load(string sku, bool showDeleted)
        {
            Parameters parmeters = new Parameters();
            parmeters.Add(new Parameter("SKU", sku));
            parmeters.Add(new Parameter("Deleted", showDeleted));
            return DataAccess.GetSingle<Product>("LoadProduct", System.Data.CommandType.StoredProcedure, parmeters);
        }
        #endregion

        #region Get Images
        /// <summary>
        /// Gets product images
        /// </summary>
        /// <param name="showDeleted">Show deleted</param>
        /// <returns>Collection images</returns>
        public virtual Images GetImages(bool showDeleted)
        {
            return Images.Load(this.Guid, showDeleted);
        }
        #endregion

        #region Get Product Catgeories
        /// <summary>
        /// Get product categories
        /// </summary>
        /// <param name="showDeleted">Show deleted</param>
        /// <returns>Collection of categories</returns>
        public virtual Categories GetCategories(bool showDeleted)
        {
            return Categories.Load(this, showDeleted);
        }
        #endregion
        
        #region Check if product linked to category
        /// <summary>
        /// Check if product is linked to a category
        /// </summary>
        /// <param name="category">Category to check</param>
        /// <param name="showDeleted">Show deleted</param>
        /// <returns>Returns true is a link is found, false if not</returns>
        public virtual bool HasCategoryLink(Category category, bool showDeleted)
        {
            foreach (Category cat in this.GetCategories(showDeleted))
            {
                if (cat.Guid == category.Guid)
                {
                    return true;
                }
            }

            return false;
        }
        #endregion

        #region Get Attributes
        /// <summary>
        /// Get attributes
        /// </summary>
        /// <returns>Collection of attributes</returns>
        public virtual Attributes GetAttributes()
        {
            return Attributes.Load(this);
        }
        #endregion

        #region Save Method
        /// <summary>
        /// Save product to database
        /// </summary>
        public virtual void Save()
        {
            DataAccess.NonQuery("SaveProduct", System.Data.CommandType.StoredProcedure, GetSaveParameters());
        }
        #endregion

        #region Delete Method
        /// <summary>
        /// Delete product from database
        /// </summary>
        public virtual void Delete()
        {
            Parameters parameters = new Parameters();
            parameters.Add(new Parameter("Guid", this.Guid));
            DataAccess.NonQuery("DeleteProduct", System.Data.CommandType.StoredProcedure, parameters);
        }
        #endregion

        #region Fill Method
        /// <summary>
        /// Fill from database
        /// </summary>
        /// <param name="reader">Data reader</param>
        public virtual void Fill(System.Data.IDataReader reader)
        {
            this.Cost = Helper.ToDecimal(reader["Cost"]);
            this.Guid = Helper.ToGuid(reader["Guid"]);
            this.Description = Helper.ToString(reader["Description"]);
            this.ManufacturerGuid = Helper.ToGuid(reader["ManufacturerGuid"]);
            this.manufacturerPartNumber = Helper.ToString(reader["ManufacturerPartNumber"]);
            this.name = Helper.ToString(reader["Name"]);
            this.Price = Helper.ToDecimal(reader["Price"]);
            this.sku = Helper.ToString(reader["SKU"]);
            this.SupplierGuid = Helper.ToGuid(reader["SupplierGuid"]);
            this.supplierPartNumber = Helper.ToString(reader["SupplierPartNumber"]);
            this.Active = Helper.ToBool(reader["Active"]);
            this.Stock = Helper.ToInt(reader["Stock"].ToString());
            this.Taxable = Helper.ToBool(reader["Taxable"]);
        }
        #endregion

        #region Get Save Parameters
        /// <summary>
        /// Get save parameters
        /// </summary>
        /// <returns>Parameter collection</returns>
        public Parameters GetSaveParameters()
        {
            Parameters parameters = new Parameters();
            parameters.Add(new Parameter("Guid", this.Guid));
            parameters.Add(new Parameter("SKU", this.SKU));
            parameters.Add(new Parameter("Name", this.Name));
            parameters.Add(new Parameter("Description", this.Description));
            parameters.Add(new Parameter("Cost", this.Cost));
            parameters.Add(new Parameter("Price", this.Price));
            parameters.Add(new Parameter("ManufacturerPartNumber", this.ManufacturerPartNumber));
            parameters.Add(new Parameter("ManufacturerGuid", this.ManufacturerGuid));
            parameters.Add(new Parameter("SupplierPartNumber", this.SupplierPartNumber));
            parameters.Add(new Parameter("SupplierGuid", this.SupplierGuid));
            parameters.Add(new Parameter("Active", this.Active));
            parameters.Add(new Parameter("Stock", this.Stock));
            parameters.Add(new Parameter("Taxable", this.Taxable));
            return parameters;
        }
        #endregion
    }
}
